/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package br.edu.fatec.ads.javacard;

import java.util.List;
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;

/**
 *
 * @author Bruno Moura
 */
public class SmartCardConnector {

    //"Fabrica" de terminais
    private TerminalFactory factory = null;
    //Lista de leitores PC/SC
    private List terminals;
    //Terminal PC/SC
    private CardTerminal terminal;
    //Smart Card
    private Card card;
    //Smart Card ATR
    private ATR cardATR;
    //Canal de Comunicação com o Smart Card
    private CardChannel cardChannel;
    //APDU de Comando
    private CommandAPDU commandAPDU;
    //APDU de Resposta
    private ResponseAPDU responseAPDU;

    private static SmartCardConnector instance = null;

    private SmartCardConnector() {
    }

    public static SmartCardConnector getInstance(){
        if(instance == null){
            instance = new SmartCardConnector();
        }

        return instance;
    }

    public CardTerminal conectarLeitora(){
        try {
            //Adquire Fabrica de Leitores
            factory = TerminalFactory.getDefault();

            //Adquire Lista de Leitores PC/SC no Sistema
            terminals = factory.terminals().list();

            System.out.println("Lista de Terminais: " + terminals);

            //Adquire Primeiro Terminal da Lista
            terminal = (CardTerminal)terminals.get(0);
            
            System.out.println("Terminal Selecionado: "
                    + terminal.getName());

            card = terminal.connect("*");

        }catch(Exception cause){
            cause.printStackTrace();
        }

        return terminal;
    }

    public void acessarApplet(byte[] AID)
        throws CardException{
        commandAPDU = new CommandAPDU(0x00, 0xA4, 0x04, 0x00, AID);

        cardChannel = card.getBasicChannel();

        responseAPDU = cardChannel.transmit(commandAPDU);

        if(responseAPDU.getSW() != 0x9000){
            throw new CardException("Problemas para selecionar o applet SW:" + responseAPDU.getSW());
        }else{
            System.out.println("Applet acessado com sucesso - SW 0X9000");
        }
    }

    public byte[] acessarMetodo(byte[] apdu)
        throws CardException{

        commandAPDU = new CommandAPDU(apdu);

        responseAPDU = cardChannel.transmit(commandAPDU);

        if(responseAPDU.getSW() != 0x9000){
            throw new CardException("Problemas para a execução do método - SW: " + responseAPDU.getSW());
        }

        System.out.println("Status Word: " + responseAPDU.getSW());

        return responseAPDU.getData();
    }

    public byte[] acessarMetodo(byte[] cabecalho, int tamanhoResposta)
        throws CardException{

        commandAPDU = new CommandAPDU(cabecalho[0], cabecalho[1], cabecalho[2], cabecalho[3], tamanhoResposta);
        
        responseAPDU = cardChannel.transmit(commandAPDU);

        if(responseAPDU.getSW() != 0x9000){
            throw new CardException("Problemas para a execução do método - SW: " + responseAPDU.getSW());
        }

        return responseAPDU.getData();
    }

    public byte[] acessarMetodo(byte[] cabecalho, byte[] dados, int tamanhoResposta)
        throws CardException{

        commandAPDU = new CommandAPDU(cabecalho[0], cabecalho[1], cabecalho[2], cabecalho[3], dados, tamanhoResposta);

        responseAPDU = cardChannel.transmit(commandAPDU);

        if(responseAPDU.getSW() != 0x9000){
            throw new CardException("Problemas para a execução do método - SW: " + responseAPDU.getSW());
        }

        return responseAPDU.getData();
    }

    public int acessarMetodo(byte[] cabecalho, byte[] dados)
        throws CardException{

        commandAPDU = new CommandAPDU(cabecalho[0], cabecalho[1], cabecalho[2], cabecalho[3], dados);

        responseAPDU = cardChannel.transmit(commandAPDU);

        return responseAPDU.getSW();
    }

    public static void main(String[] args) {
//        byte[] applet = new byte[]{ (byte) 0xf1, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x00, 0x00 };
        byte[] applet = new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00 };
        byte[] apdu = new byte[]{  0x10, 0x52, 0x00, 0x00, 0x00};
        byte[] metodo = new byte[] { 0x10, 0x53, 0x00, 0x00, 0x02, 0x03, 0x00 };
//        byte[] apduAcesso = new byte[]{  0x20, 0x01, 0x00, 0x00, 0x00};
//        byte[] metodoAcesso = new byte[] { 0x20, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00 };
        byte[] apduAcesso = new byte[]{  0x20, 0x03, 0x00, 0x00, 0x00};
        byte[] metodoAcesso = new byte[] { 0x20, 0x04, 0x00, 0x00, 0x04, 0x06, 0x00, 0x07, 0x00 };
        byte[] metodoSala = new byte[] { 0x10, 0x51, 0x00, 0x00 };
        byte[] dados = new byte[] { 0x03, 0x00 };
        int tamanhoRespostaEsperado = 0x02;

        try{

            SmartCardConnector.getInstance().conectarLeitora();
            SmartCardConnector.getInstance().acessarApplet(applet);

            byte[] resultado = SmartCardConnector.getInstance().acessarMetodo(metodo);

            System.out.println("Resultado: " + instance.formatBuffer(resultado, resultado.length));

            resultado = SmartCardConnector.getInstance().acessarMetodo(apdu);

            System.out.println("Resultado: " + instance.formatBuffer(resultado, resultado.length));

        }catch(CardException cause){
            cause.printStackTrace();
        }
    }

        /**
     * Converte Buffer bytes para String
     * @param buffer
     * @param length
     * @return String
     */
    public String formatBuffer(byte[] buffer,int length)
    {
        StringBuilder strBuff = new StringBuilder("");
        for (int i = 0; i < length; i++) {
            strBuff.append(String.format("%02X", buffer[i]));
        }
        return strBuff.toString();
    }

    public int convertByteArrayToInt(byte[] bytes){
        int valor = 0;

        for (int i = 0; i < bytes.length; i++) {
            valor += (bytes[i] & 0xff) << (8 * i);
        }

        return valor;
    }

}